/*
 * Copyright (c) 2008-2009 Ravenhearte Design
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'Ravenhearte Design' nor the names of its contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.ravenhearte.hardboild.gamelogic;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

import org.ravenhearte.hardboild.assets.NodeAsset;
import org.ravenhearte.hardboild.assets.NodeAsset.AssetType;
import org.ravenhearte.hardboild.gamelogic.SkillSet.Type;
import org.ravenhearte.hardboild.gamelogic.items.*;
import org.ravenhearte.hardboild.gui.*;
import org.ravenhearte.hardboild.gui.Element.Alignment;

import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.radakan.jme.mxml.anim.MeshAnimationController;
import com.radakan.jme.mxml.anim.OgreMesh;

public class GameCharacter {
	private String name, currSkillSet, currSkill;
	private Spatial spatial;
	private MeshAnimationController animControl;
	private ImposterElement portrait;
	TextElement willpowerText;
	private Container turnMenu, skillPick;
	private HashMap<String, Button> buttons;
	private LinkedList<Container> scrollables;
	private int[] location;
	private GameTile tile;
	private Hat hat;
	private Boots boots;
	private Gloves gloves;
	private Weapon weapon;
	private HashMap<String, SkillSet> skillSets;
	private int level, experience, skillPoints, willpower, wounds;
	private Direction direction;
	public enum Direction { SW, W, NW, NE, E, SE };
	
	public GameCharacter(String name) {
		this.name = name;

		NodeAsset body = new NodeAsset(name + "'s Body", AssetType.OUTLINEDMODEL);
		body.load("EggBody");
		animControl = body.getAnim();
		
		Geometry child = (Geometry)((Node)body.get().getChild(0)).getChild(0);
		child.setSolidColor(ColorRGBA.white);
		
		spatial = new Node(name);
		((Node)spatial).attachChild(body.get());
		
		setDirection(Direction.SE);
		
		portrait = new ImposterElement(name+"'s Portrait", 275);
		refreshPortrait();
		
		skillSets = new HashMap<String, SkillSet>();
	}
	
	public GameCharacter(String name, ColorRGBA color) {
		this(name);
		
		Geometry child = (Geometry) ((Node)((Node)((Node)spatial).getChild(0)).getChild(0)).getChild(0);
		child.setSolidColor(color);
		
		setDirection(Direction.NW);
		
		refreshPortrait();
	}
	
	public String getName() {
		return name;
	}
	
	public Spatial getSpatial() {
		return spatial;
	}
	
	public boolean isAnimating() {
		return ( (spatial.getControllerCount() > 0) ||
				(animControl.getActiveAnimation().compareTo("Stand") != 0) );
	}
	
	public Container getTurnMenu(boolean newTurn) {
		if(newTurn) {
			turnMenu = new Container(name + "'s Turn Menu", 0, 568, 500, 200);
			turnMenu.setBorderedBackground("titlescreen.png", 0, 768);
			turnMenu.toggleManagedLayout();
			
			TextElement characterName = new TextElement(name +"'s Name", ColorRGBA.black, 45);
			characterName.setAlignment(Alignment.CENTERED);
			characterName.print(name);
			characterName.setLocation(100, 10);
			turnMenu.addElement(characterName);
			
			portrait.setAlignment(Alignment.CENTERED);
			portrait.setSize(100, 100);
			portrait.setLocation(100, 45);
			turnMenu.addElement(portrait);
			
			willpowerText = new TextElement(name +"'s Willpower", ColorRGBA.black, 30);
			willpowerText.setAlignment(Alignment.CENTERED);
			turnMenu.addElement(willpowerText);
			
			buttons = new HashMap<String, Button>();
			scrollables = new LinkedList<Container>();
			
			skillPick = new Container(name + "'s Skill Pick", 200, 0, 300, 200);
			skillPick.toggleScrollable();
			
			scrollables.add(skillPick);
			Button[] scrollButtons = skillPick.getButtons();

			buttons.put(scrollButtons[0].getName(), scrollButtons[0]);
			buttons.put(scrollButtons[1].getName(), scrollButtons[1]);
			
			Iterator<String> it = skillSets.keySet().iterator();
			
			while(it.hasNext()) {
				String skillSet = it.next();
				TextButton temp = new TextButton("SKILLSET_" + skillSet, ColorRGBA.black, 25);
				temp.setAlignment(Alignment.CENTERED);
				temp.print(skillSet);
				temp.setState("MouseOut", ColorRGBA.black);
				temp.setState("MouseOver", ColorRGBA.white);
				temp.setState("MouseClick", ColorRGBA.darkGray);
				buttons.put(temp.getName(), temp);
				skillPick.addElement(temp);
			}
			
			turnMenu.addElement(skillPick);
		}
		
		willpowerText.print("Willpower: "+willpower);
		willpowerText.setLocation(100, 145);
		
		return turnMenu;
	}
	
	public void selectSkillSet(String setName) {
		currSkillSet = setName;
		buttons = new HashMap<String, Button>();
		scrollables = new LinkedList<Container>();
		
		Container skillPick = new Container(name + "'s Skill Pick", 200, 0, 300, 200);
		skillPick.toggleScrollable();
		
		scrollables.add(skillPick);
		Button[] scrollButtons = skillPick.getButtons();

		buttons.put(scrollButtons[0].getName(), scrollButtons[0]);
		buttons.put(scrollButtons[1].getName(), scrollButtons[1]);
		
		Iterator<String> it = skillSets.get(setName).getKnownSkills().keySet().iterator();
		
		while(it.hasNext()) {
			String skillName = it.next();
			Skill skill = getSkill(setName, skillName);
			if (skill.getType() == Type.ACTIVE) {
				TextButton temp = new TextButton("SKILL_" + skillName, ColorRGBA.black, 25);
				temp.setAlignment(Alignment.CENTERED);
				temp.print(skillName);
				temp.setState("MouseOut", ColorRGBA.black);
				temp.setState("MouseOver", ColorRGBA.white);
				temp.setState("MouseClick", ColorRGBA.darkGray);
				buttons.put(temp.getName(), temp);
				skillPick.addElement(temp);
			}
		}
		
		TextButton temp = new TextButton("BACK_SKILLSET", ColorRGBA.black, 25);
		temp.setAlignment(Alignment.CENTERED);
		temp.print("Back");
		temp.setState("MouseOut", ColorRGBA.black);
		temp.setState("MouseOver", ColorRGBA.white);
		temp.setState("MouseClick", ColorRGBA.darkGray);
		buttons.put(temp.getName(), temp);
		skillPick.addElement(temp);
		
		turnMenu.deleteElement(this.skillPick);
		this.skillPick = skillPick;
		turnMenu.addElement(skillPick);
	}
	
	public String getCurrSkillSet() {
		return currSkillSet;
	}
	
	public boolean selectSkill(String skillName) {
		if (getSkill(currSkillSet, skillName).getCost() > willpower) {
			return false;
		}
		currSkill = skillName;
		buttons = new HashMap<String, Button>();
		scrollables = new LinkedList<Container>();
		
		Container skillPick = new Container(name + "'s Skill Pick", 200, 0, 300, 200);
		
		TextElement txt = new TextElement("Cost", ColorRGBA.red, 25);
		txt.setAlignment(Alignment.CENTERED);
		txt.print("Cost: "+getSkill(currSkillSet, skillName).getCost());
		skillPick.addElement(txt);
		
		
		TextButton temp = new TextButton("BACK_SKILL", ColorRGBA.black, 25);
		temp.setAlignment(Alignment.CENTERED);
		temp.print("Back");
		temp.setState("MouseOut", ColorRGBA.black);
		temp.setState("MouseOver", ColorRGBA.white);
		temp.setState("MouseClick", ColorRGBA.darkGray);
		buttons.put(temp.getName(), temp);
		skillPick.addElement(temp);
		
		turnMenu.deleteElement(this.skillPick);
		this.skillPick = skillPick;
		turnMenu.addElement(skillPick);
		
		return true;
	}
	
	public String getCurrSkill() {
		return currSkill;
	}
	
	public void increaseWill(int amt) {
		willpower += amt;
	}
	
	public int getWill() {
		return willpower;
	}
	
	public void learnSkillSet(SkillSet skillSet) {
		skillSets.put(skillSet.getName(), skillSet);
	}
	
	public void increaseSkill(String skillSet, String skill, int amt) {
		if (skillSets.get(skillSet).getKnownSkills().containsKey(skill)) {
			skillSets.get(skillSet).increaseSkill(skill, amt);
		}
		else {
			skillSets.get(skillSet).learnSkill(skill, amt);
		}
	}

	public Skill getSkill(String skillSet, String skill) {
		return skillSets.get(skillSet).getSkill(skill);
	}
	
	public HashMap<String, Button> getButtons() {
		return buttons;
	}
	
	public LinkedList<Container> getScrollables() {
		return scrollables;
	}
	
	public void setLocation(int[] location) {
		this.location = location;
	}
	
	public void setDirection(Direction direction) {
		this.direction = direction;
		switch(direction) {
		case SW:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					FastMath.PI / 6, new Vector3f(0, 0, 1)));
			break;
		case W:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					FastMath.HALF_PI, new Vector3f(0, 0, 1)));
			break;
		case NW:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					5 * FastMath.PI / 6, new Vector3f(0, 0, 1)));
			break;
		case NE:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					7 * FastMath.PI / 6, new Vector3f(0, 0, 1)));
			break;
		case E:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					3 * FastMath.HALF_PI, new Vector3f(0, 0, 1)));
			break;
		case SE:
			spatial.setLocalRotation(new Quaternion().fromAngleAxis(
					11 * FastMath.PI / 6, new Vector3f(0, 0, 1)));
			break;
		}
	}
	
	public void setDirection(float direction) {
		float tolerance = .3f;
		if(FastMath.abs(direction - FastMath.PI / 6) < tolerance) {
			setDirection(Direction.SW);
		}
		else if(FastMath.abs(direction - FastMath.HALF_PI) <  tolerance) {
			setDirection(Direction.W);
		}
		else if(FastMath.abs(direction - 5 * FastMath.PI / 6) < tolerance) {
			setDirection(Direction.NW);
		}
		else if(FastMath.abs(direction - 7 * FastMath.PI / 6) < tolerance) {
			setDirection(Direction.NE);
		}
		else if(FastMath.abs(direction - 3 * FastMath.HALF_PI) < tolerance) {
			setDirection(Direction.E);
		}
		else if(FastMath.abs(direction - 11 * FastMath.PI / 6) < tolerance) {
			setDirection(Direction.SE);
		}
	}
	
	public int[] getLocation() {
		return location;
	}
	
	public Direction getDirection() {
		return direction;
	}
	
	public void setTile(GameTile tile) {
		this.tile = tile;
	}
	
	public GameTile getTile() {
		return tile;
	}

	public void equipHat(Hat hat) {
		this.hat = hat;
		((Node)spatial).attachChild(hat.getSpatial());
		takeControl((Node)hat.getSpatial());
		refreshPortrait();
	}

	public void unequipHat() {
		((Node)spatial).detachChild(hat.getSpatial());
		hat = null;
		refreshPortrait();
	}

	public void equipBoots(Boots boots) {
		this.boots = boots;
		((Node)spatial).attachChild(boots.getSpatial());
		takeControl((Node)boots.getSpatial());
		refreshPortrait();
	}

	public void unequipBoots() {
		((Node)spatial).detachChild(boots.getSpatial());
		boots = null;
		refreshPortrait();
	}

	public void equipGloves(Gloves gloves) {
		this.gloves = gloves;
		((Node)spatial).attachChild(gloves.getSpatial());
		takeControl((Node)gloves.getSpatial());
		refreshPortrait();
	}

	public void unequipGloves() {
		((Node)spatial).detachChild(gloves.getSpatial());
		gloves = null;
		refreshPortrait();
	}

	public void equipWeapon(Weapon weapon) {
		this.weapon = weapon;
		((Node)spatial).attachChild(weapon.getSpatial());
		takeControl((Node)weapon.getSpatial());
		refreshPortrait();
	}

	public void unequipWeapon() {
		((Node)spatial).detachChild(weapon.getSpatial());
		weapon = null;
		refreshPortrait();
	}
	
	private void takeControl(Node equipment) {
		equipment.getChild(0).clearControllers();
    	animControl.addMesh((OgreMesh)((Node) equipment.getChild(0)).getChild(0));
    	equipment.getChild(1).clearControllers();
    	animControl.addMesh((OgreMesh)((Node) equipment.getChild(1)).getChild(0));
	}
	
	public ImposterElement getPortrait() {
		return portrait;
	}
	
	private void refreshPortrait() {
		spatial.updateRenderState();
		Direction tmpDir = direction;
		setDirection(Direction.SE);
		portrait.setTexture(spatial);
		setDirection(tmpDir);
	}
}
